/*
 * JOGRE (Java Online Gaming Real-time Engine) - Spades
 * Copyright (C) 2004  Bob Marks (marksie531@yahoo.com)
 * http://jogre.sourceforge.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.jogre.mayi.client;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.jogre.client.awt.JogreComponent;
import org.jogre.common.games.Card;

/**
 * Component for making a bid for a spades hand
 *
 * @author Garrett Lehman (Gman)
 * @version Alpha 0.2.3
 */
public class MayIPlayAreaComponent extends JogreComponent {

	public final static boolean TEST_DISABLE_VALIDATION = true;
	
	public final static int TYPE_RUN = 0;
	public final static int TYPE_GROUP = 1;

	private List<Card> cards = new ArrayList<Card>();
	
	private List<Card> cardsClicked = new ArrayList<Card>();
		
	private List<Card> newCardsAdded = new ArrayList<Card>();
	
	private int type = -1;
	
	/**
	 * Default constructor which takes a spades model
	 *
	 * @param spadesModel
	 *            Link to the main spades model
	 */
	public MayIPlayAreaComponent() {
		
		int cardHeight = Card.CARD_PIXEL_HEIGHT;
		int cardWidth = Card.CARD_PIXEL_WIDTH;
		int cardSpacing = Card.CARD_SPACING;

		int width = (cardSpacing * 13);
		int height = cardHeight + cardSpacing;

		setPreferredSize(new Dimension(width, height));
		repaint();
	}

	/**
	 * Refresh the component.
	 *
	 * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
	 */
	public void paintComponent(Graphics g) {
		// draw background
		drawBackground(g);

		// draw cards on table
		drawCards(g);
	}

	/**
	 * Draw background
	 *
	 * @param g
	 *            Graphics
	 */
	public void drawBackground(Graphics g) {
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, getWidth(), getHeight());
		
		
		String text = "";
		if (this.type == TYPE_RUN) {
			text = "Run";
		}
		else if (this.type == TYPE_GROUP) {
			text = "Group";
		}
		else {
			text = "Not Used";
		}
		
		Font letterFont = MayILookAndFeel.TEXT_FONT;
		Graphics2D g2d = (Graphics2D) g;
		
		g2d.setFont(letterFont);
		FontRenderContext frc = g2d.getFontRenderContext();
		Rectangle2D bounds = letterFont.getStringBounds(text, frc);
		LineMetrics metrics = letterFont.getLineMetrics(text, frc);
		float width = (float) bounds.getWidth(); // The width of our text
		float lineheight = metrics.getHeight(); // Total line height
		float ascent = metrics.getAscent(); // Top of text to baseline
		float x0 = (float) ((this.getWidth() - width) / 2);
		float y0 = (float) ((this.getHeight() - lineheight) / 2 + ascent);

		g2d.setColor(MayILookAndFeel.TEXT_COLOUR);
		g2d.drawString(text, x0, y0);
	}

	public synchronized void drawCards(Graphics g) {
		int i = 0;
		for (Card c : cards) {
			int x = i * Card.CARD_SPACING;
			int y = Card.CARD_SPACING;

			y -= 0 * Card.CARD_SPACING; //XXX

			c.paintComponent(g, x, y);
			i++;
		}
	}

	/**
	 * Get card index by mouse x and y coordinates
	 *
	 * @param mouseX
	 *            Mouse x coordinate
	 * @param mouseY
	 *            Mouse y coordinate
	 * @return the card index by mouse x and mouse y
	 */
	public synchronized Card getCard(int mouseX, int mouseY) {
		int width = this.getWidth();
		int height = this.getHeight();

		Point topLeftPoint = new Point(0, 0);
		Point bottomRightPoint = new Point(width, height);

		if (mouseX < topLeftPoint.x || mouseX > bottomRightPoint.x
				|| mouseY < topLeftPoint.y || mouseY > bottomRightPoint.y) {
			return null;
		}


		int x = 0;
		int y = 0;
		Point topLeftCardPoint = null;
		Point bottomRightCardPoint = null;
		for (int i = (this.cards.size()-1); i >= 0; i--) {
			Card card = this.cards.get(i);
			
			x = i * Card.CARD_SPACING;
			y = Card.CARD_SPACING;
			if (cardsClicked.contains(card)) {
				y = 0;
			}
			topLeftCardPoint = new Point(x, y);

			x += Card.CARD_PIXEL_WIDTH;
			y += Card.CARD_PIXEL_HEIGHT;
			bottomRightCardPoint = new Point(x, y);

			if (mouseX > topLeftCardPoint.x
					&& mouseX < bottomRightCardPoint.x
					&& mouseY > topLeftCardPoint.y
					&& mouseY < bottomRightCardPoint.y) {
				return card;
			}
		}
		return null;
	}
	
	public synchronized boolean playCards(List<Card> cards) {
		if (isValidPlay(cards)) {
			this.newCardsAdded.addAll(cards);
			this.cards.addAll(cards);
			Collections.sort(this.cards, new CardComparator());
			this.repaint();
			return true;
		}
		return false;
	}
	
	public synchronized boolean valid() {
		if (TEST_DISABLE_VALIDATION) {
			return true;
		}
		
		if (this.type == TYPE_GROUP) {
			return isValidGroup();
		}
		else if (this.type == TYPE_RUN) {
			return isValidRun();
		}
		return false;
	}
	
	public synchronized boolean isValidPlay(List<Card> newCards) {
		
		if (TEST_DISABLE_VALIDATION) {
			return true;
		}
		
		
		if (newCards.isEmpty()) {
			return false;
		}
		
		if (this.type == TYPE_GROUP) {
			return isValidPlayGroup(newCards);
		}
		else if (this.type == TYPE_RUN) {
			return isValidPlayRun(newCards);
		}
		return false;
	}
	
	private boolean isValidPlayRun(List<Card> newCards) {
			
		List<Card> testRun = new ArrayList<Card>();
		testRun.addAll(this.cards);
		testRun.addAll(newCards);
		
		if (testRun.size() < 4) {
			return false;
		}
		
		Collections.sort(testRun, new CardComparator());
		
		Iterator<Card> cardIt = testRun.iterator();
		Card c = cardIt.next();
		char suit = c.getSuit();
		int value = c.getOrderValue();
		
		while (cardIt.hasNext()) {
			value++;
			c = cardIt.next();
			if (c.getSuit() != suit || c.getOrderValue() != value) {
				return false;
			}
		}
		
		return true;
	}
	
	private boolean isValidRun() {
		return isValidPlayRun(new ArrayList<Card>());
	}
	
	private boolean isValidPlayGroup(List<Card> newCards) {		
		int value = -1;
		if (!this.cards.isEmpty()) {
			value = this.cards.get(0).getOrderValue();
		}
		else {
			if (newCards.size() < 3) {
				return false;
			}
			value = newCards.get(0).getOrderValue();
		}
		
		for (Card c : newCards) {
			if (c.getOrderValue() != value) {
				return false;
			}
		}
		
		return true;
	}
	
	private boolean isValidGroup() {		
		if (this.cards.isEmpty()) {
			return false;
		}
		int value = this.cards.get(0).getOrderValue();
		
		for (Card c : this.cards) {
			if (c.getOrderValue() != value) {
				return false;
			}
		}
		
		return true;
	}
	
	public synchronized List<Card> getNewCardsAdded() {
		List<Card> newCards = new ArrayList<Card>(this.newCardsAdded);
		this.newCardsAdded.clear();
		return newCards;
	}
	
	public synchronized List<Card> unplayCards() {
		List<Card> newCards = getNewCardsAdded();
		this.cards.removeAll(newCards);
		this.repaint();
		return newCards;
	}
	
	public void setType(int type) {
		this.type = type;
		this.repaint();
	}

	public int getType() {
		return type;
	}
	
	public boolean didPlay() {
		return !this.cards.isEmpty();
	}
	
	public void reset() {
		this.cards.clear();
		this.newCardsAdded.clear();
		this.cardsClicked.clear();
		this.type = -1;
		
		this.repaint();
	}
	
	
}